package cn.tedu.csmall.stock.webapi.controller;

import cn.tedu.csmall.commons.exception.CoolSharkServiceException;
import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO;
import cn.tedu.csmall.commons.restful.JsonResult;
import cn.tedu.csmall.commons.restful.ResponseCode;
import cn.tedu.csmall.stock.service.IStockService;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/base/stock")
@Api(tags = "库存管理模块")
public class StockController {

    @Autowired
    private IStockService stockService;

    @PostMapping("/reduce/count")
    @ApiOperation("减少商品库存数")
    // @SentinelResource注解需要标记为控制层方法上,在该方法运行后,会被Sentinel监测
    // 该方法运行前,Sentinel监测不到,必须至少运行一次后才可以开始监测信息
    // "减少商品库存数"这个名称就是显示在Sentinel上该方法的名称
    // blockHandler就是只当被限流时，要运行自定义方法的属性，"blockError"就是方法名
    @SentinelResource(value = "减少商品库存数",blockHandler = "blockError",fallback = "fallbackError")
    public JsonResult reduceCommodityCount(
                        StockReduceCountDTO stockReduceCountDTO){
        // 调用业务逻辑层
        stockService.reduceCommodityCount(stockReduceCountDTO);
        if (Math.random()<0.5){
            throw new CoolSharkServiceException(ResponseCode.INTERNAL_SERVER_ERROR,
                    "随机异常");
        }
        return JsonResult.ok("库存减少完成!");
    }

    // Sentinel自定义限流方法定义规则如下
    // 1.访问修饰符必须是public
    // 2.返回值类型必须和控制器方法一致
    // 3.方法名必须是控制器方法注解中blockHandler定义的名称
    // 4.方法参数必须包含控制器的所有参数，而且可以额外添加BlockException的异常类型参数
    public JsonResult blockError(StockReduceCountDTO stockReduceCountDTO,
                                 BlockException e){
        // 运行这个方法表示当前请求被Sentinel限流了,需要给出被限流的提示
        return JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,"服务器忙，请稍后再试");
    }

    // 降级方法: 由@SentinelResource注解fallback属性指定的方法
    // 方法定义规则和限流方法基本一致，方法的参数中，最后的参数可以额外添加Throwable类型
    // 当控制器方法发生异常时，Sentinel会调用这个方法，优先级比统一异常处理类高
    // 实际开发中肯包含一些业务例如:运行老版本代码，或使用户获得一些补偿
    public JsonResult fallbackError(StockReduceCountDTO stockReduceCountDTO,
                                    Throwable throwable){
        // 我们的业务仅仅是输出异常提示
        return JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR,"运行发生异常，服务降级");
    }

}
